
#include "../inc/Object_pri.h"
#if (OOC_CFG_SW_DEBUG == OOC_SWITCH_ON)
#include "../inc/mem_leak.h"
#endif

OOC_CLASS_META(OBJECT_PKG, Object, OBJECT_PKG, Object);


#if (OOC_CFG_SW_DEBUG == OOC_SWITCH_ON)
static void* dbg_mleak_malloc(size_t nSize, muc8* pFileName, mu32 nLine);
static void dbg_mleak_free(void* cthis, void* pObj);
static void dbg_mleak_report(MLeakReportLevelDef eLevel);
#endif


//............static function...........................................
static void release(void* cthis);
static mbool instanceof(void* cthis, muc8* sTypeName);

//...........public function...............................................
static muc8* toString(_Object* cthis);
static mbool equal(_Object* cthis, _Object* oPeer);

//...........protected function....................................
static void finalize(_Object* cthis);

//..........local function.................................................
static _Object* defInit(_Object* cthis);



//##########################################################################################
void OOC_CLASS_INIT_FUNC_NAME(_Object)(void){
#if (OOC_CFG_SW_DEBUG == OOC_SWITCH_ON)
    OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList = malloc(sizeof(CListDef));
    if(MNULL == OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList){
        fprintf(stderr, "#DBG[ERROR]: <%s %s>malloc failed! ", __FILE__,  __LINE__);
        exit(EXIT_FAILURE);
    }
    
    CList_Init(OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList, MNULL);
    
    OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.malloc = dbg_mleak_malloc;
    OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.free = dbg_mleak_free;
    OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.report = dbg_mleak_report;
    
#endif      
    
    OOC_CLASS_STA_VNAME(_Object).release = release;
    OOC_CLASS_STA_VNAME(_Object).instanceof = instanceof;
}



//======================================================================================
static void release(void* cthis) {
    assert(MNULL != cthis);

    OOC_ClassLinkDef* pLink = OOC_GET_LINK_BY_CTHIS(cthis);    
    
    pLink->pCore->tCtrlBlk.tFlags.bReleased = MTRUE;
    
    
    if(0 == pLink->pCore->tCtrlBlk.tFlags.nRefCNT){
        pLink->pCore->tCtrlBlk.finalize(pLink->pCore->cthis);
    }
}


static mbool instanceof(void* cthis, muc8* sTypeName){
    assert(MNULL != cthis);
    assert(MNULL != sTypeName);

    OOC_ClassMetaDef* pClassMeta = OOC_GET_LINK_BY_CTHIS(cthis)->pCore->pMetaData;
    
    do{
        if(0 == strcmp(pClassMeta->sName, sTypeName))
            return MTRUE;
        
        if(pClassMeta == pClassMeta->pSuper)
            break;
        
        pClassMeta = pClassMeta->pSuper;
        
        
        
    }while(MTRUE != MFALSE);
    
    
    return MFALSE;
}

//##############################################################################################
_Object* OOC_CTOR_NAME(_Object)(void) {


#if OOC_CFG_SW_DEBUG == OOC_SWITCH_ON

    CListDef* pDbgNode = malloc(sizeof (CListDef));
    if (MNULL == pDbgNode) {
        fprintf(stderr, "#DBG[ERROR]: <%s %s>malloc failed! ", __FILE__, __LINE__);
        exit(EXIT_FAILURE);
    }

    OOC_ClassDbgBlkDef* pDbgBlk = malloc(sizeof (OOC_ClassDbgBlkDef));
    if (MNULL == pDbgNode) {
        fprintf(stderr, "#DBG[ERROR]: <%s %s>malloc failed! ", __FILE__, __LINE__);
        exit(EXIT_FAILURE);
    }


    pDbgBlk->pObjCore = MNULL;
    if (MLeak_Init(&pDbgBlk->tMLeak.tDetector) == MFALSE) {
        fprintf(stderr, "#DBG[ERROR]: <%s %s>malloc failed! ", __FILE__, __LINE__);
        exit(EXIT_FAILURE);
    }

    CList_Init(pDbgNode, pDbgBlk);
    CList_AppendToRight(OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList, pDbgNode);

#endif

    OOC_ClassCoreDef* pCore = MNULL;
    OOC_PUB_BLOCK_TNAME(_Object)* pPubBlock = MNULL;
    OOC_CLASS_PRO_TNAME(_Object)* pProtected = MNULL;
    _Object* cthis = MNULL;
    
    pCore = (OOC_ClassCoreDef*) OOC_MALLOC(sizeof (OOC_ClassCoreDef));

    if (MNULL == pCore) {
        goto error;
    }

    pPubBlock = (OOC_PUB_BLOCK_TNAME(_Object)*)OOC_MALLOC(sizeof (OOC_PUB_BLOCK_TNAME(_Object)));

    if (MNULL == pPubBlock) {
        goto error;            
    }
    
    pProtected = (OOC_CLASS_PRO_TNAME(_Object)*)OOC_MALLOC(sizeof (OOC_CLASS_PRO_TNAME(_Object)));

    if (MNULL == pProtected) {
        goto error;
    }
    
    //......................................................................................
    memset(pCore, 0, sizeof(OOC_ClassCoreDef));
    pCore->tCtrlBlk.tFlags.bReleased = MTRUE;  
    
    
    pCore->cthis = &pPubBlock->_Object;
    pCore->pMetaData = &OOC_CLASS_META_VNAME(_Object);
    pCore->tCtrlBlk.tFlags.bReleased = MFALSE;
    pCore->tCtrlBlk.tFlags.nRefCNT = 0;
    pCore->tCtrlBlk.finalize = (void (*)(void*))finalize;
    
    pPubBlock->tLink.pCore = pCore;
    pPubBlock->tLink.pPublic = &pPubBlock->_Object;
    pPubBlock->tLink.pProtected = pProtected;
    pPubBlock->tLink.pPrivate = MNULL;
    
    cthis = defInit(&pPubBlock->_Object);
    
error:    
    if(MNULL == cthis){
        OOC_FREE(pPubBlock);
        OOC_FREE(pProtected);
        OOC_FREE(pCore);
        
    }
    
#if OOC_CFG_SW_DEBUG == OOC_SWITCH_ON
    if(MNULL == cthis){
        
        CList_Remove(pDbgNode);
        
        free(pDbgNode);
        free(pDbgBlk);
    }else{
        
        pDbgBlk->pObjCore = pCore;
        pCore->tDebug.pDbgNode = pDbgNode;
    }
#endif    
    
    
    return cthis;
}

//=================================================================================================


static _Object* defInit(_Object* cthis){
    assert(MNULL != cthis);
    OOC_PUB_BLOCK_TNAME(_Object)* pPubBlock = OOC_GET_PUB_BLOCK(cthis, OBJECT_PKG, Object);
    OOC_CLASS_PRO_TNAME(_Object)* pProtected = (OOC_CLASS_PRO_TNAME(_Object)*)pPubBlock->tLink.pProtected;
    
    cthis->toString = toString;
    cthis->equal = equal;
    
    pProtected->finalize = finalize;
    
    return cthis;
}
//..............................................................

static muc8* toString(_Object* cthis) {
    static mu8 aString[OBJECT_TO_STRING_MAX];
    
    assert(MNULL != cthis);
    
    snprintf(aString, OBJECT_TO_STRING_MAX, 
            "%s@0x%x",
            OOC_CLASS_META_VNAME(_Object).sName,
            cthis);
    
    return aString;
    
}

static mbool equal(_Object* cthis, _Object* oPeer) {
    assert(MNULL != cthis);
    assert(MNULL != oPeer);
    
    
    return (cthis == oPeer) ? MTRUE : MFALSE;
}


//....................................................................        

static void finalize(_Object* cthis) {

    assert(MNULL != cthis);

    OOC_PUB_BLOCK_TNAME(_Object)* pPubBlock = OOC_GET_PUB_BLOCK(cthis, OBJECT_PKG, Object);
    
    
    if (MTRUE == pPubBlock->tLink.pCore->tCtrlBlk.tFlags.bReleased &&
            0 == pPubBlock->tLink.pCore->tCtrlBlk.tFlags.nRefCNT) {
#if OOC_CFG_SW_DEBUG == OOC_SWITCH_ON
        CListDef* pNode = pPubBlock->tLink.pCore->tDebug.pDbgNode;
        
#endif           
        

        OOC_FREE(pPubBlock->tLink.pCore);
        OOC_FREE(pPubBlock->tLink.pPrivate);
        OOC_FREE(pPubBlock->tLink.pProtected);
        OOC_FREE(pPubBlock);
#if OOC_CFG_SW_DEBUG == OOC_SWITCH_ON

        CList_Remove(pNode);
#endif        
        
    }

}


//############################################################################################
#if OOC_CFG_SW_DEBUG == OOC_SWITCH_ON

static void* dbg_mleak_malloc(size_t nSize, muc8* pFileName, mu32 nLine) {
    assert(MNULL != pFileName);
    
    CListDef* pNode = CList_GetTheMostRight(OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList);
    if(MNULL == pNode)
        return MNULL;
    
    OOC_ClassDbgBlkDef* pDbgBlk = pNode->pUser;
    
    return MLeak_Malloc(&pDbgBlk->tMLeak.tDetector, nSize, pFileName, nLine);
}

static void dbg_mleak_free(void* cthis, void* pObj) {
    
    CListDef* pNode = MNULL;
    
    if(MNULL != cthis){
        pNode = OOC_GET_LINK_BY_CTHIS(cthis)->pCore->tDebug.pDbgNode;
    }else{
    
        pNode = CList_GetTheMostRight(OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList);
    }
    
    
    if(MNULL == pNode)
        return;
    
    OOC_ClassDbgBlkDef* pDbgBlk = pNode->pUser;

    return MLeak_Free(&pDbgBlk->tMLeak.tDetector, pObj);
    
}

static void dbg_mleak_report(MLeakReportLevelDef eLevel){
    OOC_ClassDbgBlkDef* pDbgBlk = MNULL;
    printf("\n###########Memory Leak Detector Report###########################\n");
    if(0 == CList_LengthRigth(OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList)){
        printf("(^v^) All object has been freed. (^v^) \n");
    }
    
    
    
    for(CListDef* pNode = OOC_CLASS_STA_VNAME(_Object).tDbg.tMLeak.pObjList->pRight;
        MNULL != pNode; 
        pNode = pNode->pRight){
        
        pDbgBlk = pNode->pUser;
        
        printf("\n===============[%sClass]===============\n", (muc8*)pDbgBlk->pObjCore->pMetaData->sName);
        
        MLeak_ShowReport(&pDbgBlk->tMLeak.tDetector, eLevel);
    }
    
}
#endif
